package at.fhj.app;

import java.util.ArrayList;

import com.google.android.c2dm.C2DMessaging;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import at.fhj.app.C2DM.C2DMDeviceRegistrar;
import at.fhj.app.C2DM.C2DMPrefs;
import at.fhj.app.activity.AboutActivity;
import at.fhj.app.activity.ExamsActivity;
import at.fhj.app.activity.MarksActivity;
import at.fhj.app.activity.NewsActivity;
import at.fhj.app.activity.ScheduleActivity;
import at.fhj.app.activity.ScheduleChooserActivity;
import at.fhj.app.activity.SettingsActivity;
import at.fhj.app.model.NavigationItem;
import at.fhj.app.retriever.FHPIRetriever;
import at.fhj.app.util.ConnectionInformation;

/**
 * Main Activity of FH2go
 * 
 * This activity shows the home screen with all navigation.
 * It's the software's entry point.
 * It has over 250 lines of code, which is by some considered a bad smell. But I like the smell.
 * 
 * @author Markus Deutsch <Markus.Deutsch.ITM09@fh-joanneum.at>
 */
public class MainActivity extends Activity implements OnSharedPreferenceChangeListener, OnItemClickListener {
    public ProgressDialog prog;
    private SharedPreferences prefs;
    
    private GridView grid;
    private NavigationAdapter gridAdapter;
    private ArrayList<NavigationItem> items;
    
    private static boolean isCurrentC2DMRegistrationInWork = false;
    
    /*
     * Broadcast receiver for the C2DM-Update-Intents.
     */
    private final BroadcastReceiver updateC2DMReceiver = new BroadcastReceiver() {
	
	public void onReceive(Context context, Intent intent) {
	    isCurrentC2DMRegistrationInWork = false;
	    final String statusMessage = intent.getStringExtra(C2DMDeviceRegistrar.C2DM_REGISTRATION_UPDATE_INTENT_EXTRA_STATUSMESSAGE);
	    final boolean status = intent.getBooleanExtra(C2DMDeviceRegistrar.C2DM_REGISTRATION_UPDATE_INTENT_EXTRA_STATUS, false);
	    MainActivity.this.runOnUiThread(new Runnable() {
		  public void run() {
		      // Update navigation items...
		      loadNavigationItems();
		      gridAdapter.notifyDataSetChanged();
		      grid.invalidateViews();
		      if (!status && !(statusMessage == null)) {
			      Toast.makeText(getApplicationContext(), statusMessage, Toast.LENGTH_SHORT).show();
		      }
		  }});	    
	}
    };
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        loadNavigationItems();
        
        grid = (GridView) findViewById(R.id.grid);
        gridAdapter = new NavigationAdapter(this);
        grid.setAdapter(gridAdapter);
        grid.setOnItemClickListener(this);
        
        /*
         * Initialize shared preferences and register a changeListener to 
         * address complaints by unexperienced Android users who didn't know
         * whether properties were saved or not.
         */
        prefs = PreferenceManager.getDefaultSharedPreferences(this);
        prefs.registerOnSharedPreferenceChangeListener(this);
        
        /*
         * Register as receiver for C2DM registration status updates.
         */
        registerReceiver(updateC2DMReceiver, new IntentFilter(C2DMDeviceRegistrar.C2DM_REGISTRATION_UPDATE_INTENT));
    }
	
	/**
	 * Fills the ArrayList with NavigationItems.
	 * 
	 * The IDs given to NavigationItems should not be changed over revisions, even if order changes, 
	 * because they are used to reference the functionality of the button.
	 * 
	 */
	private void loadNavigationItems(){
	        items = new ArrayList<NavigationItem>();
	        
		items.add(new NavigationItem(0, R.drawable.btn_ele_schedule, R.string.lbl_schedule));
		items.add(new NavigationItem(1, R.drawable.btn_ele_marks2, R.string.lbl_marks));
		items.add(new NavigationItem(2, R.drawable.btn_ele_exams, R.string.lbl_exams));
		
		items.add(new NavigationItem(3, R.drawable.btn_ele_news, R.string.lbl_news));
		items.add(new NavigationItem(4, R.drawable.btn_ele_help, R.string.lbl_help));

		// Because the application should be supported on devices with Android API Level >= 7 (>= 2.1)
		// we have to check here if we should display the "Notifications" button, because
		// these Push notifications depend on Google's Cloud 2 Device Messaging framework, which is supported
		// on devices with Android API Level >= 8 (>= 2.2).
		// (This app is compiled against API level 7).
		if (Build.VERSION.SDK_INT >= 8) {
		    	// For Push messaging we have to differentiate which icon/command we are using...
			// --> we are already registered
			// --> we are not registered
        		if (C2DMPrefs.get(this).contains("deviceRegistrationID")) {
        		    // Icon: ON / Command: Unregister (Id 7)
        		    items.add(new NavigationItem(7, R.drawable.push_c2dm_on, R.string.lbl_menuc2dm));
        		}
        		else {
        		    // Icon: OFF / Command: Register (Id 6)
        		    items.add(new NavigationItem(6, R.drawable.push_c2dm_off, R.string.lbl_menuc2dm));
        		}
		}

		items.add(new NavigationItem(5, R.drawable.btn_ele_settings, R.string.lbl_settings));
	}
	
	/**
	 * Find out whether username and password are set.
	 * 
	 * @return true if username and password are set
	 */
	private boolean validateData(){
		if(prefs.getString("username", "").equals("") || prefs.getString("password", "").equals("")){
			Toast.makeText(getApplicationContext(), getString(R.string.msg_incompletedata), Toast.LENGTH_LONG).show();
			return false;
		} else {
			return true;
		}
	}
	
	/**
	 * Find out whether year and course are set.
	 * 
	 * @return true if year and course are set
	 */
	private boolean validateCourseData(){
		if(prefs.getString("course", "").equals("") || prefs.getString("year", "").equals("")){
			return false;
		} else {
			return true;
		}
	}
	
	/**
	 * Make a toast after changing shared preferences.
	 */
	public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
	    // In C2DM we modify shared prefs in background threads; the problem with this
	    // toast is: It is also run in background thread, but it has to be run on UIThread.
	    // Otherwise an error can occur:
	    // "E/AndroidRuntime(370): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()"
	    // 18.05.2012: We do not use "defaultSharedPrefs" in C2DM any more, but we leave the code as it is:	    
	    this.runOnUiThread(new Runnable() {
		  public void run() {
		      Toast.makeText(getApplicationContext(), getString(R.string.msg_prefschanged), Toast.LENGTH_SHORT).show();
		    }
		  });
	}
	
	/**
	 * Catch button clicks.
	 */
	public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
		if(items.get(arg2).getId() == 5){
			Intent intent = new Intent(this, SettingsActivity.class);
			startActivity(intent);
		}
		
		if(items.get(arg2).getId() == 6){			
		    // Register C2DM
		    if (isCurrentC2DMRegistrationInWork)
			Toast.makeText(getApplicationContext(), getString(R.string.msg_c2dminprogress), Toast.LENGTH_SHORT).show();
		    else {
			isCurrentC2DMRegistrationInWork = true;
			C2DMessaging.register(this, C2DMDeviceRegistrar.SENDER_ID);
		    }
		}
		
		if(items.get(arg2).getId() == 7){
		    // Unregister C2DM
		    if (isCurrentC2DMRegistrationInWork)
			Toast.makeText(getApplicationContext(), getString(R.string.msg_c2dminprogress), Toast.LENGTH_SHORT).show();
		    else {
			isCurrentC2DMRegistrationInWork = true;			
			C2DMessaging.unregister(this);
		    }
		}
		
		if(items.get(arg2).getId() == 4){
			Intent intent = new Intent(this, AboutActivity.class);
			startActivity(intent);
		}
		
		if(items.get(arg2).getId() == 1 && validateData()){
			prog = ProgressDialog.show(this, "", getString(R.string.prog_wait), false);
			MarksRetriever r = new MarksRetriever(this);
			r.execute("Hallo");
		}
		
		if(items.get(arg2).getId() == 2 && validateData()){
			prog = ProgressDialog.show(this, "", getString(R.string.prog_wait), false);
			ExamsRetriever r = new ExamsRetriever(this);
			r.execute("Hallo");
		}
		
		if(items.get(arg2).getId() == 3){
			prog = ProgressDialog.show(this, "", getString(R.string.prog_wait), false);
			NewsRetriever r = new NewsRetriever(this);
			r.execute("Hallo");
		}
		
		/**
		 * If the course and year have been set, load schedule, if not, redirect to the
		 * selector screen.
		 */
		if(items.get(arg2).getId() == 0){
			if(!validateCourseData()){
				Intent intent = new Intent(this, ScheduleChooserActivity.class);
				startActivity(intent);
			} else {
				String course = prefs.getString("course", "");
				String year = prefs.getString("year", "");
				
				Bundle bundle = new Bundle();
				bundle.putString("course", course);
				bundle.putString("year", year);
				Intent intent = new Intent(this, ScheduleActivity.class);
				intent.putExtras(bundle);
				startActivityForResult(intent, 0);
			}
			
		}
		
	}
	
	/**
	 * Thread to retrieve marks from server and redirect to MarksActivity
	 * 
	 * @author Markus Deutsch <Markus.Deutsch.ITM09@fh-joanneum.at>
	 *
	 */
	private class MarksRetriever extends AsyncTask<String, String, String>{
		Activity a;
		String status;
		
		public MarksRetriever(Activity a){
			this.a = a;
		}

		@Override
		protected String doInBackground(String... params) {
			FHPIRetriever fr = new FHPIRetriever();
			String[] arguments = {"u", "p"};
			String[] values = {prefs.getString("username", "0"), prefs.getString("password", "0")};
			fr.prepareRequest("getmarks.php", arguments, values);
			
			return fr.retrieve();
		}
		
		protected void onPostExecute(String result) {
			status = ConnectionInformation.getConnectionStatus(result);
			prog.dismiss();
			if(status.equals("OK")){
				Bundle bundle = new Bundle();
				bundle.putString("values", result);
				Intent intent = new Intent(a, MarksActivity.class);
				intent.putExtras(bundle);
				startActivityForResult(intent, 0);
			} else {
				// If we had trouble, say the word!
				Toast.makeText(getApplicationContext(), getString(ConnectionInformation.getConnectionStatusString(status)), Toast.LENGTH_LONG).show();
			}
			
	     }
		
	}
	
	/**
	 * Thread to retrieve exams from server and redirect to ExamsActivity
	 * 
	 * @author Markus Deutsch <Markus.Deutsch.ITM09@fh-joanneum.at>
	 *
	 */
	private class ExamsRetriever extends AsyncTask<String, String, String>{
		Activity a;
		String status;
		
		public ExamsRetriever(Activity a){
			this.a = a;
		}

		@Override
		protected String doInBackground(String... params) {
			FHPIRetriever fr = new FHPIRetriever();
			String[] arguments = {"u", "p"};
			String[] values = {prefs.getString("username", "0"), prefs.getString("password", "0")};
			fr.prepareRequest("getexams.php", arguments, values);
			
			return fr.retrieve();
		}
		
		protected void onPostExecute(String result) {
			status = ConnectionInformation.getConnectionStatus(result);
			prog.dismiss();
			if(status.equals("OK")){
				Bundle bundle = new Bundle();
				bundle.putString("values", result);
				Intent intent = new Intent(a, ExamsActivity.class);
				intent.putExtras(bundle);
				startActivityForResult(intent, 0);
			} else {
				// If we had trouble, say the word!
				Toast.makeText(getApplicationContext(), getString(ConnectionInformation.getConnectionStatusString(status)), Toast.LENGTH_LONG).show();
			}
			
	     }
		
	}
	
	/**
	 * Thread to retrieve news and then redirect to NewsActivity
	 * 
	 * @author Markus Deutsch <Markus.Deutsch.ITM09@fh-joanneum.at>
	 *
	 */
	private class NewsRetriever extends AsyncTask<String, String, String>{
		Activity a;
		
		public NewsRetriever(Activity a){
			this.a = a;
		}

		@Override
		protected String doInBackground(String... params) {
			FHPIRetriever fr = new FHPIRetriever();
			fr.prepareRequest("getNews.php");
			String result = fr.retrieve();
			return result;
		}
		
		protected void onPostExecute(String result) {
			prog.dismiss();
			try {
				Bundle bundle = new Bundle();
				bundle.putString("values", result);
				Intent intent = new Intent(a, NewsActivity.class);
				intent.putExtras(bundle);
				startActivityForResult(intent, 0);
			} catch (Exception e) {
				e.printStackTrace();
				// If we had trouble, say the word!
				Toast.makeText(getApplicationContext(), getString(R.string.msg_error), Toast.LENGTH_LONG).show();
			}
			
	     }
		
	}
	
	/**
	 * NavigationAdapter for Grid Menu
	 * 
	 * @author Markus Deutsch <Markus.Deutsch.ITM09@fh-joanneum.at>
	 */
	public class NavigationAdapter extends BaseAdapter{
		Context context;
	      
		public NavigationAdapter(Context context) {
			this.context = context;
		}
	    
		/**
		 * Return total count of navigation items.
		 */
		public int getCount(){
			return items.size();
		}

		public View getView(int position, View convertView, ViewGroup parent){
		    View navigationItem = convertView;
	         
		    // See http://stackoverflow.com/questions/6277805/android-grid-view-loads-in-reverse-order
		    // we have to alway refresh the views (ignore convertView)
		    //if(convertView == null){	            
		    //Inflate the layout
		    LayoutInflater li = getLayoutInflater();
		    navigationItem = li.inflate(R.layout.navigation_item, null);
			
		    // Map layout items to objects
		    TextView text = (TextView) navigationItem.findViewById(R.id.NavigationItemText);
		    ImageView image = (ImageView) navigationItem.findViewById(R.id.NavigationItemImage);

		    // Fill Grid Elements with life!
		    text.setText(getResources().getString(items.get(position).getTitleResource()));           
		    image.setImageResource(items.get(position).getImageResource());

		    return navigationItem;
	      }

	      public Object getItem(int arg0) {
	         // TODO Auto-generated method stub
	         return null;
	      }

	      public long getItemId(int arg0) {
	         // TODO Auto-generated method stub
	         return 0;
	      }

	   }
	
}